# coding=utf-8
import shutil
from string import Template
from generator.types_hal.property import Property, TypesHal
from generator.types_hal.property_excle import gen_prop_interface_excle
import os
import re
from typing import Dict, List, Tuple
import random

try:
    import CppHeaderParser
except :
    os.system('pip3 install robotpy-cppheaderparser')
    import CppHeaderParser

from utils.util import  writeFile

types_hal_file = Template("""
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package vendor.ecarx.xma.automotive.vehicle@2.0;

import android.hardware.automotive.vehicle@2.0;

typedef android.hardware.automotive.vehicle@2.0::VehiclePropertyGroup VehiclePropertyGroup;
typedef android.hardware.automotive.vehicle@2.0::VehicleArea VehicleArea;
typedef android.hardware.automotive.vehicle@2.0::VehiclePropertyType VehiclePropertyType;

enum ECarXVehicleProperty : int32_t{

${vendor_vehicle_props}

};

enum DirectTransmitProperty : int32_t{
${direct_transmit_props}
};

${vendor_enums}
""")

property_templete=Template(
"""
    /**
     * ${description}
     *
     * Publisher   : ${publisher}
     * Subscriber  : ${subscriber}
     *
     * encodeType  : ${encodingType}
     *
     * @change_mode VehiclePropertyChangeMode:${change_mode}
     * @access VehiclePropertyAccess:${access_mode}
     */
    ${prop_name} = (
        ${prop_id}
        | VehiclePropertyGroup:VENDOR
        | VehiclePropertyType:${prop_type}
        | VehicleArea:${prop_area})
"""
)

enum_templete=Template(
"""
enum ${enum_name} : int32_t{
${enum_items}
};
"""
)

enum_item_tempele=Template(
"""
    ${name} = ${value},"""
)

prop_enum_item_tempele=Template(
"""
${name} = ${value}"""
)

# jar包释放开关
VHAL_JAR_RELEASE = False
# 释放vhal jar包的版本号
VHAL_JAR_VERSION = '104'
# property id定义规则
PROPERTY_TYPES = {'STRING':'10',
                  'BOOLEAN':'20',
                  'INT32':'40',
                  'INT32_VEC':'41',
                  'INT64':'50',
                  'INT64_VEC' :'51',
                  'FLOAT':'60',
                  'FLOAT_VEC':'61',
                  'BYTES':'70',
                  'MIXED':'e0',
                  'MASK':'ff'}

PROPERTY_AREAS = {'GLOBAL':'1',
                  'WINDOW':'3',
                  'MIRROR':'4',
                  'SEAT':'5',
                  'DOOR':'6',
                  'WHEEL':'7',
                  'MASK':'f'}

#解析当前项目中已有的sdb信号，返回信号id与名字的双向映射字典
def parseCurrentSignalIds()->Tuple[dict,dict]:
    curSignalIdsHeaderFile = "../../types.hal"

    if not os.path.exists(curSignalIdsHeaderFile):
        print(">>>>>>>>>>>>not exist types.hal")
        return ({},{})
    signalHeader = CppHeaderParser.CppHeader(curSignalIdsHeaderFile)
    signalName2IdMap = {}
    hexRegex = re.compile(r'0[xX][0-9a-fA-F]+')
    dupSigName = []
    for emitem in signalHeader.enums:
        emname = emitem["name"]
        if(emname not in ["ECarXVehicleProperty","DirectTransmitProperty"]):
            continue
        for sigitem in emitem["values"]:
            #print("propName:%s, value:%s" % (sigitem["name"],sigitem["value"]))
            propId = int(hexRegex.findall(sigitem["value"])[0],16)
            #print("id is ", propId)
            if(sigitem["name"] in signalName2IdMap):
                dupSigName.append(sigitem["name"])
            signalName2IdMap[sigitem["name"]] = propId
    signalId2NameMap = { v:k for k,v in signalName2IdMap.items() }
    if(signalId2NameMap.__len__() != signalName2IdMap.__len__()):
        diffSigNames = signalName2IdMap.keys() - signalId2NameMap.values()
        dupSigIds = set()
        for sigName in diffSigNames:
            dupSigIds.add(signalName2IdMap.get(sigName))
        print("dupids",dupSigIds)

        dupSigInfos = {}
        for sigName,sigId in signalName2IdMap.items():
            if (sigId not in dupSigIds):
                continue
            dupSigs = dupSigInfos.get(sigId, set())
            dupSigs.add(sigName)
            dupSigInfos[sigId] = dupSigs

        raise NameError("types hal id有重复:", dupSigInfos)
    if len(dupSigName) > 0 :
        raise NameError("types hal name有重复:", dupSigName)

    return (signalId2NameMap,signalName2IdMap)

#根据信号名生成id,同时startIdx
#先从历史信号头文件中查找，如找到，返回id；
#否则从起始索引逐步查找不重复的id
def genSignalIdByName(startIdx:int , sigName:str,id2NameMap:Dict[int, str] = {}, 
                      name2IdMap:Dict[str,int] = {}, rstIdx:int = 1,maxIdx:int =0xffff ):
    if(startIdx <= 0 or startIdx > maxIdx):
        startIdx = rstIdx
    retIdx = name2IdMap.get(sigName,-1)
    if(retIdx != -1):
        #print(">>>>>>>%s 沿用原id: %d" % (sigName, retIdx))
        return (retIdx,startIdx)
    retIdx = startIdx
    while(retIdx in id2NameMap):
        #print(">>>>>>%d is used by sigName:%s,find next id" % (retIdx, id2NameMap[retIdx]))
        retIdx += 1
        if(retIdx > maxIdx):
            retIdx = rstIdx
    print(">>>>>>property %s 新生成id:%d" % (sigName, retIdx))
    return (retIdx, retIdx+1)

def gen_types_hal(typesHal:TypesHal):
    (propId2NameMap,propName2IdMap) = parseCurrentSignalIds()

    vendor_vehicle_props=[]
    direct_transmit_props=[]
    normalStartIndex=random.randint(0x7000,0x7fff)
    transmitStartIndex=random.randint(0x8000,0xffff)
    for prop in typesHal.props:
        if prop.isDirectTransmit:
            (propId,transmitStartIndex) = genSignalIdByName(startIdx=transmitStartIndex, sigName=prop.prop_name,
                                                          id2NameMap=propId2NameMap,
                                                          name2IdMap=propName2IdMap,rstIdx=0x8000, maxIdx=0xffff)
            direct_transmit_props.append(property_templete.substitute(
                description=prop.description,
                publisher=prop.publisher,
                subscriber=prop.subscriber,
                encodingType=prop.encodeType,
                change_mode=prop.change_mode,
                access_mode=prop.access_mode,
                prop_name=prop.prop_name,
                prop_type=prop.prop_type,
                prop_area=prop.prop_area,
                prop_id=hex(propId)
            ))
        else:
            (propId,normalStartIndex) = genSignalIdByName(startIdx=normalStartIndex, sigName=prop.prop_name,
                                                          id2NameMap=propId2NameMap,
                                                          name2IdMap=propName2IdMap,rstIdx=0x7000, maxIdx=0x7fff)
            vendor_vehicle_props.append(property_templete.substitute(
                description=prop.description,
                publisher=prop.publisher,
                subscriber=prop.subscriber,
                encodingType=prop.encodeType,
                change_mode=prop.change_mode,
                access_mode=prop.access_mode,
                prop_name=prop.prop_name,
                prop_type=prop.prop_type,
                prop_area=prop.prop_area,
                prop_id=hex(propId)
            ))

    allPropLists:List[Property]=[]

    propStartIndex = 0x9000
    currentPropStartIndex = 0x9000
    for item in typesHal.propList:
        for propItem in item:
            (propItemId,propStartIndex) = genSignalIdByName(startIdx=propStartIndex, sigName=propItem.prop_name,
                                                          id2NameMap=propId2NameMap,
                                                          name2IdMap=propName2IdMap,rstIdx=propStartIndex, maxIdx=int(hex(propStartIndex+0x3ff),16))
            # 保存property生成的propID
            if propItem.prop_internal == False:
                propItem.setPropId('0x2'+PROPERTY_AREAS[propItem.prop_area]+PROPERTY_TYPES[propItem.prop_type]+hex(propItemId)[2:])
                allPropLists.append(propItem)

            vendor_vehicle_props.append(property_templete.substitute(
                description=propItem.description,
                publisher=propItem.publisher,
                subscriber=propItem.subscriber,
                encodingType=propItem.encodeType,
                change_mode=propItem.change_mode,
                access_mode=propItem.access_mode,
                prop_name=propItem.prop_name,
                prop_type=propItem.prop_type,
                prop_area=propItem.prop_area,
                prop_id=hex(propItemId)
            ))
        currentPropStartIndex = int(hex(currentPropStartIndex+0x400),16)
        propStartIndex = currentPropStartIndex

    vendor_enums=[]
    for enum in typesHal.enums:
        members=[]
        prop_enums=[]
        for (member,_) in enum.members:
            (name,value) = member.split("=")
            members.append(enum_item_tempele.substitute(
                name=name,
                value=value
            ))
            prop_enums.append(prop_enum_item_tempele.substitute(
                name=name,
                value=value
            ))
        vendor_enums.append(enum_templete.substitute(
            enum_name=enum.getTypeName(),
            enum_items="".join(members)
        ))
        # 有枚举值的property把定义的枚举值保存起来
        for prop in allPropLists:
            if prop.encodeType == enum.getTypeName():
                prop.setPropEnums("".join(prop_enums))
                break

    # 版本和表格由发布人员执行统一生成
    if VHAL_JAR_RELEASE:
        gen_prop_interface_excle(allPropLists)
        gen_jar_version(vendor_enums)

    writeFile("../..","types.hal",lambda file:
        file.write(types_hal_file.substitute(
            vendor_vehicle_props=",".join(vendor_vehicle_props),
            direct_transmit_props=",".join(direct_transmit_props),
            vendor_enums="".join(vendor_enums)
        )))

def gen_jar_version(vendor_enums):
    versions=[]
    versions.append(enum_item_tempele.substitute(
        name='VERSION',
        value=VHAL_JAR_VERSION
    ))
    vendor_enums.append(enum_templete.substitute(
        enum_name='ECarXPropertyVersion',
        enum_items="".join(versions)
    ))
